/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const PENDING = 'pending',
const FULFILLED = 'fulfilled',
const REJECTED = 'rejected'

class myPromise {
  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallback = [];
  failCallback = [];
  constructor(excutor) {
    //传入执行器函数，执行器函数的参数是resolve,reject方法
    try {
      excutor(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }
  // resolve,reject函数用来改变promise的状态，只能单向改变一次
  resolve = value => {
    if (this.status != PENDING) {
      return
    }
    this.status = 'FULFILLED'
    this.value = value
    // 判断是否存在成功回调，若存在就调用
    // this.successCallback && this.successCallback(this.value)
    // 异步情况下存在很多等待调用的成功回调
    while(this.successCallback.length)this.successCallback.shift()()
  }

  reject = reason => {
    if (this.status != PENDING) {
      return this
    }
    this.status = 'REJECTED'
    this.reason = reason
    // 判断是否存在失败回调，若存在就调用
    // this.failCallback && this.failCallback(this.status)
    // 异步情况下存在很多等待调用的失败回调
    while(this.failCallback.length)this.failCallback.shift()()
  }

  then(successCallback, failCallback) {
    // 让参数变成可选参数，没有参数的话就返回value或者reason到下一个Promise
    successCallback=  successCallback? successCallback  : value => value;
    failCallback = failCallback? failCallback : reason => reason;
    // 实现链式调用
    // 1.then返回promise
    // 2.第一个then的返回值作为第二个then的参数传进去。
    let promise2 = new myPromise((resolve,reject) => {
      if (this.status === FULFILLED) {
        // 执行器执行完才会有promise2,所以要异步执行以下内容
        setTimeout(() => {
          try { 
            let x = successCallback(this.value)
            resolve(promise2,x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        }, 0)
      }else if (this.status === REJECTED) {
        setTimeout(() => {
          try { 
            let x = failCallback(this.reason)
            resolve(promise2,x, resolve, reject)
          } catch(e) {
            reject(e)
          }
        }, 0)
      } else {
        //类中存在异步代码，还是等待状态，要临时存储成功回调和失败回调
        // then方法可以多次调用，多次调用如果是异步excutor的话就要存储起来所有的成功回调和失败回调到数组。
        this.successCallback.push(() => {
          setTimeout(() => {
            try { 
              let x = successCallback(this.value)
              resolve(promise2,x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try { 
              let x = failCallback(this.reason)
              resolve(promise2,x, resolve, reject)
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promise2
  }
  // 判断回调返回值是不是promise
  // 是普通值直接resolve传递给下一个promise
  // 是promise，判断promise的状态再传递。
  resolvePromise(promise2,x, resolve,reject) {
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof myPromise) {
      x.then(value => resolve(value), reason => reject(reason));
    } else {
      resolve(x)
    }
  }

  //catch 处理当前的失败回调
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  // all方法，所有promise对象都执行完后再得到结果。
  static all(array) {
    let result = []
    let index = 0;
    return new myPromise((resolve, reject) => {
      function addData(key, value) {
          result[key] = value;
          index++;
          if (index === array.length) {
            resolve(result)
          }
      }
      for (let i = 0; i < array.length; i++) {
        if (array[i] instanceof myPromise) {
          array[i].then(value => addData(i, value), reason => reject(reason));
        } else {
          addData(i,array[i])
        }
      }
    })
  }

  static resolve(value) {
    if (value instanceof myPromise) return value;
    return new myPromise(resolve => resolve(value))
  }

}

module.exports = myPromise;